package com.baturu.vin.gm.service.impl;

import com.baturu.carvin.dtos.CarTypeDTO;
import com.baturu.carvin.service.CarTypeService;
import com.baturu.carvin.service.impl.AppConfigLoader;
import com.baturu.vin.base.dto.BasePartsImageInfoDTO;
import com.baturu.vin.base.pool.EmailPool;
import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.base.util.MD5Utils;
import com.baturu.vin.dto.*;
import com.baturu.vin.dto.parts.PartsAssemblyDTO;
import com.baturu.vin.gm.consts.EngineTransmissionType;
import com.baturu.vin.gm.dal.dao.*;
import com.baturu.vin.gm.dal.entity.GMSelfRuleEntity;
import com.baturu.vin.gm.dto.*;
import com.baturu.vin.kit.StreamKit;
import com.baturu.vin.service.EPCVinCacheService;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Service("generalMotorsEPCAnalyser")
public class GeneralMotorsEPCAnalyser extends EPCAnalyser<GMVinModelDTO, GmPartsMappingDTO, GMPartsDetailDTO, EPCCarTypeDTO> implements InitializingBean {

    private final static int OFFSET = 35;
    private final static String POSITION_ON = "0";
    @Autowired
    private AppConfigLoader appConfigLoader;
    @Autowired
    private CarTypeService carTypeService;
    @Autowired
    private EPCVinCacheService epcVinCacheService;
    @Autowired
    private VinModelDAO vinModelDAO;
    @Autowired
    private PartsDetailDAO partsDetailDAO;
    @Autowired
    private EngineTransmissionDAO engineTransmissionDAO;
    @Autowired
    private CarSystemBtrIdDAO carSystemBtrIdDAO;
    @Autowired
    private GmPartsMappingDAO partsMappingDAO;
    @Autowired
    private GeneralMotorsReplaceCodeDAO generalMotorsReplaceCodeDAO;
    @Autowired
    private PartsPositionDAO partsPositionDAO;
    @Autowired
    private PartsCodeColorMapDAO partsCodeColorMapDAO;
    @Autowired
    private OptionFamilyDAO optionFamilyDAO;
    @Autowired
    private GMBtrPartsDAO gmBtrPartsDAO;
    private EmailPool emailPool = EmailPool.getInstance();

    private static Map<String, String> colorCodeDescMap = Collections.emptyMap();
    private static Map<String, String> featureDescMap = Collections.emptyMap();

    private Pattern yearOne = Pattern.compile("^\\d{4}.\\d{4}[\\s\\S]*$");
    private Pattern yearTwo = Pattern.compile("^\\d{4}[\\s\\S]*$");

    private Pattern seriesTwo = Pattern.compile("^[\\s\\S]{10}[a-zA-Z,， ]*[0-9]{2}[^0-9^-][\\S\\s]*$");
    private Pattern seriesOne = Pattern.compile("^[\\s\\S]{10}[a-zA-Z,， ]*[0-9]{3}[^0-9]{2}[\\S\\s]*$");
    private Pattern seriesThree = Pattern.compile("^[\\s\\S]{10}[a-zA-Z,， ]* [\\S\\s]*$");

    private Pattern partsNameRex = Pattern.compile("[0-9]{8}\\*[0-9]{8}");

    @Override
    public GMVinModelDTO analyseVinCode(String vinCode) {
        GMVinModelDTO vinModelDTO = vinModelDAO.findVinModel(vinCode);
        if (Objects.isNull(vinModelDTO)) {
            epcVinCacheService.addWaitCacheVinCode(EPCAnalyserEnum.GM, vinCode);
            return null;
        }
        //将家族规则set进vinOptionFaimliy
        List<String> optionCodeList = vinModelDTO.getOptionCodeList();
        Map<String, String> optionFamilyMap = StreamKit.partitionStream(optionCodeList, SEGMENT_QUERY_SIZE)
                .flatMap(list -> optionFamilyDAO.findOptionFamily(list).stream())
                .collect(Collectors.toMap(OptionFamilyDTO::getOptionCode, OptionFamilyDTO::getFamName, (v1, v2) -> v1 + "," + v2));
        if (MapUtils.isEmpty(optionFamilyMap)) {
            return vinModelDTO;
        }
        vinModelDTO.setVinOptionFamily(HashMultimap.create());
        optionCodeList.forEach(d -> {
            String famName = optionFamilyMap.get(d);
            if (StringUtils.isEmpty(famName)) {
                return;
            }
            Arrays.stream(famName.split(",")).forEach(s -> vinModelDTO.getVinOptionFamily().put(s, d));

        });

        return vinModelDTO;
    }

    @Override
    public GMVinModelDTO analyseVinCodeByCache(String vinCode) {
        GMVinModelDTO vinModelDTO = vinModelDAO.findVinModel(vinCode);
        if (Objects.isNull(vinModelDTO)) {
            return null;
        }
        //将家族规则set进vinOptionFaimliy
        List<String> optionCodeList = vinModelDTO.getOptionCodeList();
        Map<String, String> optionFamilyMap = StreamKit.partitionStream(optionCodeList, SEGMENT_QUERY_SIZE)
                .flatMap(list -> optionFamilyDAO.findOptionFamily(list).stream())
                .collect(Collectors.toMap(OptionFamilyDTO::getOptionCode, OptionFamilyDTO::getFamName, (v1, v2) -> v1 + "," + v2));
        if (MapUtils.isEmpty(optionFamilyMap)) {
            return vinModelDTO;
        }
        vinModelDTO.setVinOptionFamily(HashMultimap.create());
        optionCodeList.forEach(d -> {
            String famName = optionFamilyMap.get(d);
            if (StringUtils.isEmpty(famName)) {
                return;
            }
            Arrays.stream(famName.split(",")).forEach(s -> vinModelDTO.getVinOptionFamily().put(s, d));

        });

        return vinModelDTO;
    }

    @Override
    public List<EPCCarTypeDTO> queryCarTypes(GMVinModelDTO analyseResult) {
        CarSystemBtrIdDTO carSystemBtrIdDTO = buildCarType(analyseResult);
        if (Objects.isNull(carSystemBtrIdDTO)) {
            return Collections.emptyList();
        }
        EPCCarTypeDTO epcCarTypeDTO = new EPCCarTypeDTO();
        epcCarTypeDTO.setBtrId(carSystemBtrIdDTO.getBtrId());
        epcCarTypeDTO.setExtendAttrDTOs(Lists.newArrayList(new EPCCarTypeDTO.ExtendAttributeDTO("gm_chassis", carSystemBtrIdDTO.getChasis(), "")));
        return Lists.newArrayList(epcCarTypeDTO);
    }

    @Override
    public List<GmPartsMappingDTO> queryPartsMappings(Set<String> btrPartsNames, GMVinModelDTO analyseResult) {
        return partsMappingDAO.queryByBtrPartsName(analyseResult.getBrandName(), btrPartsNames);
    }

    @Override
    public List<GMPartsDetailDTO> queryPartsInfoByPartsMapping(List<GmPartsMappingDTO> partsMappings, GMVinModelDTO analyseResult) {
        Set<String> partsCodeList = partsMappings.stream()
                .map(GmPartsMappingDTO::getPartsCode)
                .collect(Collectors.toSet());
        return queryPartsInfoByPartsCode(partsCodeList, analyseResult);
    }

    @Override
    public List<GMPartsDetailDTO> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE)
                .flatMap(d -> partsDetailDAO.findPartsDetailsByPartsCode(d).stream())
                .collect(Collectors.toList());
    }

    @Override
    public List<GMPartsDetailDTO> queryPartsInfoByPartsCode(Set<String> partsCodes, GMVinModelDTO analyseResult) {
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE)
                .flatMap(d -> partsDetailDAO.findPartsDetails(analyseResult.getCatalog(), d).stream())
                .collect(Collectors.toList());
    }

    @Override
    public List<GMPartsDetailDTO> filterPartsInfoByVinAnalyseResult(List<GMPartsDetailDTO> partsDetailDTOs, GMVinModelDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsDetailDTOs)) {
            return Collections.emptyList();
        }
        partsDetailDTOs.removeIf(d -> !matchYear(d.getFirstYear(), d.getLastYear(), analyseResult.getYear()));
        partsDetailDTOs.removeIf(d -> matchBody(d.getBody(), analyseResult));
        partsDetailDTOs.removeIf(d -> noMatchOptionCode(analyseResult.getOptionCodeList(), d));
        partsDetailDTOs.removeIf(d -> StringUtils.isAnyBlank(d.getGroupCode(), d.getPictureNO(), d.getPartsCode()));
        if (CollectionUtils.isEmpty(partsDetailDTOs)) {
            return Collections.emptyList();
        }

        //根据图片过滤及series进行过滤
        Map<String, List<PartsImageInfoDTO>> imageMap = partsPositionDAO.queryPartsImageV2Info(analyseResult.getCatalog()).stream().collect(Collectors.groupingBy(PartsImageInfoDTO::getArtNumber));
        partsDetailDTOs.removeIf(f -> matchImage(f, analyseResult, imageMap));
        String series = analyseResult.getSeries();
        //第二位为A或者0表示通用车系
        if (StringUtils.length(series) > 1 && series.charAt(1) != 'A' && series.charAt(1) != '0') {
            partsDetailDTOs.removeIf(d -> !matchSeries(d, analyseResult));
        }

        //根据家族的规则去过滤
        if (partsDetailDTOs.stream().allMatch(d -> StringUtils.isNotBlank(d.getOptionP()))) {
            partsDetailDTOs = matchFamily(partsDetailDTOs, analyseResult);
        } else if (partsDetailDTOs.stream().anyMatch(d -> StringUtils.isNotBlank(d.getOptionP()))) {
            List<GMPartsDetailDTO> haveOption = partsDetailDTOs.stream()
                    .filter(d -> StringUtils.isNotBlank(d.getOptionP()))
                    .collect(Collectors.toList());
            haveOption = matchFamily(haveOption, analyseResult);
            partsDetailDTOs = partsDetailDTOs.stream()
                    .filter(d -> !StringUtils.isNotBlank(d.getOptionP()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(haveOption)) {
                partsDetailDTOs.addAll(haveOption);
            }
        }
        //特殊规则
        partsDetailDTOs.removeIf(this::filterByBiggerPartsName);
        return partsDetailDTOs;
    }

    private boolean filterByBiggerPartsName(GMPartsDetailDTO d) {
        return d.getPartNameCN().contains("加大") && "00".equals(d.getGroupCode());
    }

    private boolean matchBody(String bodys, GMVinModelDTO analyseResult) {
        if (StringUtils.isBlank(bodys)) {
            return false;
        }
        List<String> bodyList = Splitter.on(",").splitToList(bodys);
        String vinBody = analyseResult.getBody();
        String series = analyseResult.getSeries();

        for (String body : bodyList) {
            if (StringUtils.contains(body, series)) {
                String dealBody = body.replace(series, "");
                if (StringUtils.isBlank(dealBody) || StringUtils.contains(dealBody, analyseResult.getBody())) {
                    return false;
                }
            } else if (StringUtils.contains(body, vinBody)) {
                return false;
            }
        }
        return true;
    }

    private boolean matchImageByYear(String des, GMVinModelDTO analyseResult) {
        boolean yearBool = false;
        String year = analyseResult.getYear();
        if (StringUtils.isEmpty(year)) {
            return false;
        }
        if (yearOne.matcher(des).matches()) {
            boolean start = Integer.parseInt(des.substring(0, 4)) <= Integer.parseInt(year);
            boolean end = Integer.parseInt(des.substring(5, 9)) >= Integer.parseInt(year);
            yearBool = !(start && end);
        } else if (yearTwo.matcher(des).matches()) {
            yearBool = !des.substring(0, 4).equals(year);
        }
        return yearBool;
    }

    private boolean matchImageBySeriesAndBody(String des, GMVinModelDTO analyseResult, GMPartsDetailDTO parts) {
        Boolean seriesBool = false;
        Boolean bodyBool = false;
        String series = StringUtils.EMPTY;
        String body = StringUtils.EMPTY;
        if (seriesTwo.matcher(des).matches()) {
            String lessYear = des.substring(10, des.length());
            int start = lessYear.indexOf(Pattern.compile("[^0-9]").matcher(lessYear).replaceAll("").trim().charAt(0));
            series = lessYear.substring(0, start);
            body = lessYear.substring(start, start + 2);
        } else if (seriesOne.matcher(des).matches()) {
            String lessYear = des.substring(10, des.length());
            int start = lessYear.indexOf(Pattern.compile("[^0-9]").matcher(lessYear).replaceAll("").trim().charAt(1));
            series = lessYear.substring(0, start);
            body = lessYear.substring(start, start + 2);
        } else if (seriesThree.matcher(des).matches()) {
            String lessYear = des.substring(10, des.length());
            series = lessYear.split(" ")[0];
        }

        if (StringUtils.isNotEmpty(series)) {
            if (series.length() == 1) {
                seriesBool = false;
            } else if (series.length() > 1 && !series.contains("，") && !series.contains(" ")) {
                HashSet<String> seriesSet = Sets.newHashSet(series.split(","));
                if (seriesSet.stream().allMatch(d -> d.length() > 1)) {
                    seriesBool = !seriesSet.contains(analyseResult.getSeries());
                } else {
                    seriesBool = false;
                }
            } else if (series.length() > 1 && series.contains("，")) {
                HashSet<String> seriesSet = Sets.newHashSet(series.split("，"));
                if (seriesSet.stream().allMatch(d -> d.length() > 1)) {
                    seriesBool = !seriesSet.contains(analyseResult.getSeries());
                } else {
                    seriesBool = false;
                }
            } else if (series.length() > 1 && series.contains(",") && series.contains(" ")) {
                HashSet<String> seriesSet = Sets.newHashSet(series.split(", "));
                if (seriesSet.stream().allMatch(d -> d.length() > 1)) {
                    seriesBool = !seriesSet.contains(analyseResult.getSeries());
                } else {
                    seriesBool = false;
                }
            }
        } else {
            seriesBool = false;
        }

        if (StringUtils.isNotEmpty(body) && StringUtils.isNotEmpty(parts.getBody())) {
            List<String> bodyList = Lists.newArrayList(analyseResult.getBody());
            bodyBool = !bodyList.contains(body);
        }
        return bodyBool || seriesBool;
    }

    private boolean matchImage(GMPartsDetailDTO parts, GMVinModelDTO analyseResult, Map<String, List<PartsImageInfoDTO>> imageMap) {
        String subject = parts.getSubject();
        if (StringUtils.isEmpty(subject)) {
            return false;
        }
        List<PartsImageInfoDTO> list = imageMap.get(subject);
        if (CollectionUtils.isEmpty(list)) {
            return false;
        }
        boolean yearBool = false;
        boolean seriesBodyBool = false;
        for (PartsImageInfoDTO p : list) {
            String des = p.getDes();
            if (StringUtils.isEmpty(des)) {
                return false;
            }
            yearBool = matchImageByYear(des, analyseResult);
            seriesBodyBool = matchImageBySeriesAndBody(des, analyseResult, parts);

            if (!yearBool && !seriesBodyBool) {
                return false;
            }
        }

        return yearBool || seriesBodyBool;
    }

    private List<GMPartsDetailDTO> matchFamily(List<GMPartsDetailDTO> partsDetailDTOs, GMVinModelDTO analyseResult) {
        List<String> optionCodeList = analyseResult.getOptionCodeList();
        Map<String, String> optionFamilyMap = StreamKit.partitionStream(optionCodeList, SEGMENT_QUERY_SIZE)
                .flatMap(list -> optionFamilyDAO.findOptionFamily(list).stream())
                .collect(Collectors.toMap(OptionFamilyDTO::getOptionCode, OptionFamilyDTO::getFamName, (v1, v2) -> v1 + "," + v2));
        if (MapUtils.isEmpty(optionFamilyMap)) {
            return ListUtils.EMPTY_LIST;
        }

        HashMultimap<String, String> vinOptionFamilyMap = analyseResult.getVinOptionFamily();
        if (vinOptionFamilyMap == null || vinOptionFamilyMap.size() == 0) {
            return ListUtils.EMPTY_LIST;
        }
        partsDetailDTOs.removeIf(d -> !matchOptionCode(d, analyseResult, vinOptionFamilyMap));

        //全满足和部分满足进行筛选
        Set<String> pass = analyseResult.getPass();
        Set<String> alternative = analyseResult.getAlternative();
        if (CollectionUtils.isEmpty(alternative)) {
            return partsDetailDTOs;
        }
        if (CollectionUtils.isNotEmpty(pass)) {
            alternative.removeIf(d -> pass.stream().anyMatch(d::equals));
            partsDetailDTOs.removeIf(d -> alternative.contains(d.getPartsCode()));
        } else {
            partsDetailDTOs.removeIf(d -> !alternative.contains(d.getPartsCode()));
        }
        return partsDetailDTOs;
    }

    private boolean matchOptionCode(GMPartsDetailDTO parts, GMVinModelDTO analyseResult, HashMultimap<String, String> vinOptionFamilyMap) {
        List<String> optionPList = getOptionCodeList(parts.getOptionP());
        if (CollectionUtils.isEmpty(optionPList)) {
            return true;
        }

        HashMultimap<String, String> multimap = groupByOptionCode(optionPList);
        if (multimap.isEmpty()) {
            return true;
        }

        Set<String> familyNames = multimap.keySet();
        boolean filter = false;
        boolean alternative = false;
        boolean pass = false;
        for (String familyName : familyNames) {
            Set<String> name = vinOptionFamilyMap.get(familyName);
            if (CollectionUtils.isNotEmpty(Sets.intersection(multimap.get(familyName), name))) {
                pass = true;
            } else if (CollectionUtils.isEmpty(name)) {
                alternative = true;
            } else {
                filter = true;
                break;
            }
        }
        if (!filter && !alternative && pass) {
            analyseResult.getPass().add(parts.getPartsCode());
        } else if (alternative && !filter) {
            analyseResult.getAlternative().add(parts.getPartsCode());
        } else {
            return false;
        }
        return true;
    }

    private HashMultimap<String, String> groupByOptionCode(List<String> optionList) {
        HashMultimap<String, String> multimap = HashMultimap.create();
        Map<String, String> optionFamilyMap = StreamKit.partitionStream(optionList, SEGMENT_QUERY_SIZE)
                .flatMap(list -> optionFamilyDAO.findOptionFamily(list).stream())
                .collect(Collectors.toMap(OptionFamilyDTO::getOptionCode, OptionFamilyDTO::getFamName, (v1, v2) -> v1 + "," + v2));
        optionList.forEach(d -> {
            String famName = optionFamilyMap.getOrDefault(d, null);
            if (StringUtils.isEmpty(famName)) {
                return;
            }
            Arrays.stream(famName.split(",")).forEach(s -> multimap.put(s, d));
        });
        return multimap;
    }

    private boolean matchSeries(GMPartsDetailDTO d, GMVinModelDTO analyseResult) {
        String seriesResult = analyseResult.getSeries();
        String series = d.getSeries();
        if (StringUtils.isAnyBlank(series, seriesResult)) {
            return true;
        }
        List<String> seriesList = Splitter.on(",").splitToList(series);
        return seriesList.stream().anyMatch(s -> s.contains(seriesResult) || s.charAt(1) == 'A' || s.charAt(1) == '0');
    }

    private boolean noMatchOptionCode(List<String> vinOptionCode, GMPartsDetailDTO partsDetailDTO) {
        boolean noMatchOptionP = false;
        boolean noMatchOptionN = false;
        //正选装码 如果没有就pass，如果有且全部不含在车架的选装码中就过滤
        if (StringUtils.isNotBlank(partsDetailDTO.getOptionP()) && getOptionCodeList(partsDetailDTO.getOptionP()).stream().noneMatch(vinOptionCode::contains)) {
            noMatchOptionP = true;
        }
        //负选装码，如果没有就pass，如果有且有一个或以上含在车架选装码中就过滤
        if (StringUtils.isNotBlank(partsDetailDTO.getOptionN()) && getOptionCodeList(partsDetailDTO.getOptionN()).stream().anyMatch(vinOptionCode::contains)) {
            noMatchOptionN = true;
        }

        return noMatchOptionP || noMatchOptionN;
    }

    private List<String> getOptionCodeList(String str) {
        if (StringUtils.isEmpty(str)) {
            return Lists.newArrayList();
        }
        str = str.replace("&", "");
        str = str.replace(",", "");
        return Splitter.fixedLength(3).splitToList(str);
    }

    @Override
    public Map<String, List<GMPartsDetailDTO>> filterPartsInfoByVinAnalyseResult(Map<String, List<GMPartsDetailDTO>> partsInfoGroupByPartsName, GMVinModelDTO analyseResult) {
        Map<String, List<GMPartsDetailDTO>> resultMap = filterByMeasurement(partsInfoGroupByPartsName);
        resultMap.values().forEach(partsList -> {
            if (partsList.size() > 1) {
                List<GMPartsDetailDTO> filter = partsList.stream().filter(d -> !filterByPartsName(d)).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(filter)) {
                    partsList = filter;
                }
                partsList.removeIf(this::filterByPartsName);
            }
            if (partsList.size() == 2) {
                //为了带出唯一
                filterByOptionCode(partsList);
            }
        });
        return filterBySelfRule(resultMap);
    }

    private void filterByOptionCode(List<GMPartsDetailDTO> detailDTO) {
        GMPartsDetailDTO firstPartsDetailDTO = detailDTO.get(0);
        GMPartsDetailDTO secondPartsDetailDTO = detailDTO.get(1);
        if (firstPartsDetailDTO.getOptionPList().contains(secondPartsDetailDTO.getOptionN())) {
            detailDTO.removeIf(d->d.getPartsCode().equals(firstPartsDetailDTO.getPartsCode()));
        }
        if (secondPartsDetailDTO.getOptionPList().contains(firstPartsDetailDTO.getOptionN())) {
            detailDTO.removeIf(d->d.getPartsCode().equals(secondPartsDetailDTO.getPartsCode()));
        }
    }

    private boolean filterByPartsName(GMPartsDetailDTO parts) {
        String partNameCN = parts.getPartNameCN();
        Matcher matcher = partsNameRex.matcher(partNameCN);
        String partsCode = parts.getPartsCode();
        if (!matcher.matches()) {
            return false;
        }
        if (matcher.find()) {
            String partsName = matcher.group();
            List<String> partsCodes = Splitter.on("*").trimResults().splitToList(partsName);
            partsCode = partsCodes.get(1);
        }
        return !parts.getPartsCode().equals(partsCode);
    }

    private Map<String, List<GMPartsDetailDTO>> filterBySelfRule(Map<String, List<GMPartsDetailDTO>> resultMap) {
        if (CollectionUtils.isEmpty(resultMap.keySet())) {
            return resultMap;
        }
        List<GMSelfRuleEntity> ruleEntityList = partsDetailDAO.findSelfRule(resultMap.keySet());
        ruleEntityList.forEach(d -> {
            List<GMPartsDetailDTO> gmPartsDetailDTOS = resultMap.get(d.getBtrPartsName());
            List<String> partsCodes = gmPartsDetailDTOS.stream().map(GMPartsDetailDTO::getPartsCode).collect(Collectors.toList());
            if (d.getPartsCodeList().containsAll(partsCodes)) {
                gmPartsDetailDTOS.removeIf(q -> !d.getBtrPartsCode().equals(q.getPartsCode()));
                resultMap.put(d.getBtrPartsName(), gmPartsDetailDTOS);
            }
        });
        return resultMap;
    }

    private Map<String, List<GMPartsDetailDTO>> filterByMeasurement(Map<String, List<GMPartsDetailDTO>> partsInfoGroupByPartsName) {
        Map<String, List<GMPartsDetailDTO>> resultMap = Maps.newHashMap();
        Set<String> somePartsName = Sets.newHashSet("活塞", "活塞环", "曲轴瓦", "连杆瓦");
        //过滤四个主名的非标准尺寸编码
        partsInfoGroupByPartsName = partsInfoGroupByPartsName.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> {
            List<GMPartsDetailDTO> partsDetailDTOS = e.getValue();
            if (somePartsName.contains(e.getKey())) {
                partsDetailDTOS.removeIf(d -> StringUtils.containsAny(d.getPartNameCN(), "加大", "缩小"));
            }
            return partsDetailDTOS;
        }));
        partsInfoGroupByPartsName.forEach((partsName, partsInfo) -> {
            if (partsInfo.size() > 1) {
                List<GMPartsDetailDTO> partsInfoByFilter = partsInfo.stream().filter(d -> StringUtils.equals(d.getFuturePart(), "Y")).collect(Collectors.toList());
                if (!partsInfoByFilter.isEmpty()) {
                    partsInfo = partsInfoByFilter;
                }
            }
            resultMap.put(partsName, partsInfo);
        });
        return resultMap;
    }

    @Override
    protected List<GmPartsMappingDTO> queryPartsMappingByPartsInfoAndAnalyseResult(Map<String, List<GMPartsDetailDTO>> partsInfoGroup, GMVinModelDTO analyseResult) {
        if (Objects.isNull(analyseResult)) {
            return Collections.emptyList();
        }
        Set<String> partsCodes = partsInfoGroup.values().stream()
                .flatMap(l -> l.stream().map(GMPartsDetailDTO::getPartsCode))
                .collect(Collectors.toSet());
        return partsMappingDAO.queryByPartsCode(analyseResult.getBrandName(), partsCodes);
    }

    @Override
    protected Map<String, String> queryColorRemark(Map<String, List<GMPartsDetailDTO>> partsInfoGroup, GMVinModelDTO analyseResult) {
        return partsInfoGroup.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
                .flatMap(p -> getOptionCodeList(p.getOptionN()).stream()).map(colorCodeDescMap::get)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.joining(","))));
    }

    @Override
    protected Map<String, Set<String>> queryFeatures(Map<String, List<GMPartsDetailDTO>> partsInfoGroup, GMVinModelDTO analyseResult) {
        return partsInfoGroup.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
                .flatMap(p -> getOptionCodeList(p.getOptionP()).stream()).map(featureDescMap::get)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet())));
    }

    @Override
    protected Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<GMPartsDetailDTO>> partsInfoGroup, GMVinModelDTO analyseResult) {
        Map<String, List<PartsPositionDTO>> partsPositionToMap = Maps.newHashMap();
        for (Map.Entry<String, List<GMPartsDetailDTO>> entry : partsInfoGroup.entrySet()) {
            List<PartsPositionQueryParam> queryParams = entry.getValue().stream()
                    .filter(d -> StringUtils.equals(d.getKit(), POSITION_ON)).map(input -> PartsPositionQueryParam.builder().subject(input.getSubject()).catalogCode(input.getCatalogCode()).partNo(input.getPictureNO()).build())
                    .distinct()
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(queryParams)) {
                continue;
            }
            List<PartsPositionDTO> partsPositions = StreamKit.partitionStream(queryParams, SEGMENT_QUERY_SIZE)
                    .flatMap(d -> partsPositionDAO.findPartsPositions(d).stream())
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(partsPositions)) {
                continue;
            }
            partsPositionToMap.put(entry.getKey(), partsPositions);
        }

        Set<String> subjectSet = partsInfoGroup.values().stream()
                .flatMap(Collection::stream)
                .map(GMPartsDetailDTO::getSubject).collect(Collectors.toSet());
        Set<PartsImageInfoDTO> partsImageInfoDTOs = Collections.EMPTY_SET;
        if (CollectionUtils.isNotEmpty(subjectSet)) {
            partsImageInfoDTOs = partsPositionDAO.queryPicDesBySubject(subjectSet);
        }
        Map<String, String> desMap = partsImageInfoDTOs.stream().collect(Collectors.toMap(PartsImageInfoDTO::getArtNumber, PartsImageInfoDTO::getDes, (v1, v2) -> v1));
        return partsInfoGroup.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e ->
                        e.getValue().stream()
                                .map(p -> p.getSubject() + "_" + p.getPartsCode())
                                .distinct()
                                .map(key -> {
                                    String[] arr = key.split("_");
                                    String imageCode = arr[0];
                                    String partsCode = arr[1];

                                    EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                                    epcPartsImageDTO.setImageUrl(appConfigLoader.getPartsPicBaseUrl() + "generalmotors/" + imageCode + ".gif");
                                    epcPartsImageDTO.setImageName(desMap.get(imageCode));
                                    epcPartsImageDTO.setImageCode(imageCode);

                                    List<PartsPositionDTO> partsPositionDTOs = partsPositionToMap.get(partsCode);
                                    if (CollectionUtils.isNotEmpty(partsPositionDTOs)) {
                                        List<PartsPositionDTO> collect = partsPositionDTOs.stream().filter(d -> StringUtils.equals(d.getSubject(), imageCode)).collect(Collectors.toList());
                                        epcPartsImageDTO.setEpcPartsImagePositionDTOs(buildPosition(collect));
                                    }
                                    return epcPartsImageDTO;
                                }).distinct().collect(Collectors.toList())));

    }

    private List<EPCPartsImagePositionDTO> buildPosition(List<PartsPositionDTO> positions) {
        if (CollectionUtils.isNotEmpty(positions)) {
            return positions.stream().map(i -> {
                EPCPartsImagePositionDTO epcPartsImagePositionDTO = new EPCPartsImagePositionDTO();
                epcPartsImagePositionDTO.setPosition(i.getPositionNo());

                String positionX = StringUtils.isNotBlank(i.getPositionX()) ? i.getPositionX() : "0";
                String positionY = StringUtils.isNotBlank(i.getPositionY()) ? i.getPositionY() : "0";

                epcPartsImagePositionDTO.setTopLeft(new EPCImagePointDTO(Integer.parseInt(positionX), Integer.parseInt(positionY)));
                epcPartsImagePositionDTO.setBottomRight(new EPCImagePointDTO(Integer.parseInt(positionX) + OFFSET, Integer.parseInt(positionY) + OFFSET));
                return epcPartsImagePositionDTO;
            }).distinct().collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @Override
    public Map<String, Set<String>> queryReplaceCodes(Set<String> partsCodes, GMVinModelDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyMap();
        }
        List<ReplaceCodeDTO> replaceCodeDTOS = generalMotorsReplaceCodeDAO.queryGeneralMotorsReplaceCode(partsCodes);
        if (CollectionUtils.isEmpty(replaceCodeDTOS)) {
            return Collections.emptyMap();
        }
        return replaceCodeDTOS.stream().collect(Collectors.toMap(ReplaceCodeDTO::getPartsCode, dto -> StringUtils.isNotEmpty(dto.getReplaceCodes()) ? Sets.newHashSet(dto.getReplaceCodes().split(",")) : Collections.emptySet()));
    }

    @Override
    public Map<String, String> queryInstallNum(Map<String, List<GMPartsDetailDTO>> partsInfoGroup, GMVinModelDTO analyseResult) {
        return partsInfoGroup.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
                entry -> String.join(",", entry.getValue().stream()
                        .filter(d -> StringUtils.isNumeric(d.getQuantity()))
                        .filter(d -> StringUtils.isNotBlank(d.getQuantity()))
                        .map(d -> Integer.parseInt(d.getQuantity()) + "")
                        .collect(Collectors.toSet()))));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        List<OptionCodeTranslateDTO> partsCodeColorMapDTOs = partsCodeColorMapDAO.findPartsCodeColorMap();
        if (CollectionUtils.isNotEmpty(partsCodeColorMapDTOs)) {
            colorCodeDescMap = partsCodeColorMapDTOs.stream().filter(d -> d.getType().equals("1")).collect(Collectors.toMap(OptionCodeTranslateDTO::getOptionCode, OptionCodeTranslateDTO::getDescription, (v1, v2) -> v1));
            featureDescMap = partsCodeColorMapDTOs.stream().filter(d -> d.getType().equals("2")).collect(Collectors.toMap(OptionCodeTranslateDTO::getOptionCode, OptionCodeTranslateDTO::getDescription, (v1, v2) -> v1));
        }
    }

    public CarSystemBtrIdDTO buildCarType(GMVinModelDTO vinModelDTO) {
        if (Objects.isNull(vinModelDTO)) {
            return null;
        }
        CarSystemBtrIdDTO carSystemBtrIdDTO = carSystemBtrIdDAO.findCarSystemBtrId(vinModelDTO.getCatalog(), vinModelDTO.getYear());
        if (Objects.isNull(carSystemBtrIdDTO)) {
            String btrId = "GMEPC_" + MD5Utils.getMD5Code(vinModelDTO.getCatalog()) + vinModelDTO.getYear();

            List<CarSystemBtrIdDTO> carSystemBtrIdByChasis = carSystemBtrIdDAO.findCarSystemBtrIdByChasis(vinModelDTO.getCatalog());
            if (CollectionUtils.size(carSystemBtrIdByChasis) == 1) {
                carSystemBtrIdDTO = carSystemBtrIdByChasis.get(0);
                carSystemBtrIdDTO.setMj(vinModelDTO.getYear());
                carSystemBtrIdDTO.setBtrId(btrId);
                carSystemBtrIdDAO.insertNewCarType(carSystemBtrIdDTO);
            } else {
                String mes = String.format("通用：插入新车型时，出现重复车系或者有新增车系，请核查。vincode:%s,chasis:%s,mj:%s", vinModelDTO.getVinCode(), vinModelDTO.getCatalog(), vinModelDTO.getYear());
                log.warn(mes);
                emailPool.putEmailToQueue(mes);
                return null;
            }

        }
        fillEngineAndTransmission(vinModelDTO, carSystemBtrIdDTO);
        saveCarType(vinModelDTO, carSystemBtrIdDTO);
        return carSystemBtrIdDTO;
    }

    private void fillEngineAndTransmission(GMVinModelDTO vinModelDTO, CarSystemBtrIdDTO carSystemBtrIdDTO) {
        StringBuilder btrId = new StringBuilder(carSystemBtrIdDTO.getBtrId());
        if (Objects.isNull(vinModelDTO)) {
            return;
        }
        String optionCode = vinModelDTO.getOptionCode();
        if (StringUtils.isBlank(optionCode)) {
            log.warn("通用：选装码为空，请检查vin:{}", vinModelDTO.getVinCode());
            return;
        }
        Set<String> optionCodeList = Sets.newHashSet(StringUtils.split(optionCode, ","));
        if (CollectionUtils.isEmpty(optionCodeList)) {
            log.warn("通用：选装码为空，请检查vin:{}", vinModelDTO.getVinCode());
            return;
        }
        List<EngineTransmissionDTO> engineTransmissionDTOs = engineTransmissionDAO.findEngineTransmissions(optionCodeList);
        if (CollectionUtils.isEmpty(engineTransmissionDTOs)) {
            String mes = String.format("通用：缺失发动机和变速箱的选装码翻译，请检查vin:%s，btrid:%s", vinModelDTO.getVinCode(), btrId.toString());
            log.warn(mes);
            emailPool.putEmailToQueue(mes);
            return;
        }
        List<EngineTransmissionDTO> engineDTOs = engineTransmissionDTOs.stream()
                .filter(d -> d.getType() == EngineTransmissionType.ENGINE)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(engineDTOs)) {
            EngineTransmissionDTO engineTransmissionDTO = engineDTOs.stream().findFirst().get();
            vinModelDTO.setEngine(engineTransmissionDTO.getOptionCode());

            String autoManual = engineTransmissionDTO.getAutoManual();
            String parameter = engineTransmissionDTO.getParameter();

            if (StringUtils.isNotBlank(autoManual)) {
                vinModelDTO.setDisplacement(autoManual);
                vinModelDTO.setEngineDesc(autoManual + " ");
            }
            if (StringUtils.isNotBlank(parameter)) {
                vinModelDTO.setEngineDesc(vinModelDTO.getEngineDesc() + parameter);
            }
            btrId.append(engineTransmissionDTO.getOptionCode());
        }

        List<EngineTransmissionDTO> transmissionDTOs = engineTransmissionDTOs.stream()
                .filter(d -> d.getType() == EngineTransmissionType.TRANSMISSION)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(transmissionDTOs)) {
            EngineTransmissionDTO engineTransmissionDTO = transmissionDTOs.stream().findFirst().get();
            vinModelDTO.setTransmission(engineTransmissionDTO.getOptionCode());

            String autoManual = engineTransmissionDTO.getAutoManual();
            String parameter = engineTransmissionDTO.getParameter();

            if (StringUtils.isNotBlank(autoManual)) {
                vinModelDTO.setTransmissionDesc(autoManual + " ");
            }
            if (StringUtils.isNotBlank(parameter)) {
                vinModelDTO.setTransmissionDesc(vinModelDTO.getTransmissionDesc() + parameter);
            }
            btrId.append(engineTransmissionDTO.getOptionCode());
        }
        btrId.append(vinModelDTO.getYear());
        carSystemBtrIdDTO.setBtrId(btrId.toString());
    }

    private int saveCarType(GMVinModelDTO vinModelDTO, CarSystemBtrIdDTO carSystemBtrIdDTO) {
        String btrId = carSystemBtrIdDTO.getBtrId();
        if (StringUtils.isBlank(btrId)) {
            return 0;
        }
        List<CarTypeDTO> carTypeDTOs = carTypeService.findCarTypeByBtrIds(Lists.newArrayList(btrId));
        if (CollectionUtils.isNotEmpty(carTypeDTOs)) {
            return 0;
        }

        return epcVinCacheService.saveNewCarType(Lists.newArrayList(CarTypeBuilderDTO
                .builder()
                .btrId(btrId)
                .btrBrand(carSystemBtrIdDTO.getBrand())
                .btrSubBrand(carSystemBtrIdDTO.getSubBrand())
                .carSystemId(carSystemBtrIdDTO.getCarSystemId())
                .salesName(vinModelDTO.getEngineDesc() + " " + vinModelDTO.getTransmissionDesc())
                .chassis(vinModelDTO.getSeries())
                .displacement(vinModelDTO.getDisplacement())
                .carSystem(carSystemBtrIdDTO.getCarSystem())
                .productTime(vinModelDTO.getYear())
                .engineType(vinModelDTO.getEngine())
                .transmissionType(vinModelDTO.getTransmission())
                .transmissionDesc(vinModelDTO.getTransmissionDesc())
                .build()));
    }

    private boolean matchYear(String start, String end, String year) {
        if (StringUtils.isBlank(start)) {
            start = "0";
        }
        if (StringUtils.isBlank(end)) {
            end = "9999";
        }
        return Integer.parseInt(start) <= Integer.parseInt(year) && Integer.parseInt(year) <= Integer.parseInt(end);
    }

    @Override
    public List<GMPartsDetailDTO> queryPartsInfoByImageCode(Set<String> imageCodes, GMVinModelDTO analyseResult) {
        return filterPartsInfoByVinAnalyseResult(StreamKit.partitionStream(imageCodes, SEGMENT_QUERY_SIZE)
                .flatMap(l -> partsPositionDAO.queryPartsDetail(analyseResult.getCatalog(), l).stream())
                .collect(Collectors.toList()), analyseResult);
    }

    @Override
    public List<EPCPartsImageDTO> queryEPCImageInfoByImageCode(Set<String> imageCodes, GMVinModelDTO analyseResult) {
        Map<String, List<GMPartsDetailDTO>> partsInfoGroup = queryPartsInfoByImageCode(imageCodes, analyseResult).stream().collect(Collectors.groupingBy(getPartsGroupKeyForPartsMappingFunction()));
        return queryPartsImage(partsInfoGroup, analyseResult).values().stream().flatMap(Collection::stream).collect(Collectors.toList());
    }

    @Override
    protected Map<String, String> queryEpcPartsName(Map<String, List<GMPartsDetailDTO>> partsInfoGroup, GMVinModelDTO analyseResult) {
        return partsInfoGroup.values().stream()
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(GMPartsDetailDTO::getPartsCode, GMPartsDetailDTO::getPartNameCN, (v1, v2) -> v1));
    }

    @Override
    public Map<String, List<BasePartsImageInfoDTO>> queryEPCImageInfo(Set<String> sortNames, GMVinModelDTO analyseResult) {
        List<PartsImageInfoDTO> partsImageInfo = StreamKit.partitionStream(sortNames, SEGMENT_QUERY_SIZE)
                .flatMap(l -> partsPositionDAO.queryPartsImageInfo(l, analyseResult.getCatalog()).stream())
                .collect(Collectors.toList());

        return partsImageInfo.stream()
                .collect(Collectors.groupingBy(PartsImageInfoDTO::getSortName, Collectors.toSet()))
                .entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey,
                        entry -> queryEPCImageInfoByImageCode(entry.getValue().stream().map(PartsImageInfoDTO::getArtNumber).collect(Collectors.toSet()), analyseResult)
                                .stream()
//                                .map(d ->  new GMPartsImageInfoDTO(d.getImageCode(), d.getImageName(), d.getImageUrl()))
                                .map(d -> d.getImageCode() + "@" + d.getImageUrl() + "@" + d.getImageName())
                                .distinct()
                                .map(d -> {
                                    String[] s = d.split("@");
                                    String imageCode = s[0];
                                    String imageUrl = s[1];
                                    String imageName = s[2];
                                    return new GMPartsImageInfoDTO(imageCode, imageName, imageUrl);
                                })
//                                .distinct()
                                .map(d -> (BasePartsImageInfoDTO) d)
                                .collect(Collectors.toList())));
    }

    @Override
    protected List<EPCPartsDTO> getEPCPartsDTOListFromBtrPartsByPartsName(Set<String> btrPartsNames, GMVinModelDTO analyseResult) {
        if (CollectionUtils.isEmpty(btrPartsNames) || analyseResult == null) {
            return Collections.emptyList();
        }

        //非油类自编码
        Set<GMBtrPartsDTO> btrPartsInfo = queryBtrParts(btrPartsNames, analyseResult);

        if (CollectionUtils.isEmpty(btrPartsInfo)) {
            return Collections.emptyList();
        }

        return btrPartsInfo.stream()
                .filter(Objects::nonNull)
                .map(p -> {
                    EPCPartsDTO epcPartsDTO = new EPCPartsDTO();
                    epcPartsDTO.setBtrPartsName(p.getBtrPartsName());
                    epcPartsDTO.setPartsCode(p.getPartsCode());
                    if (p.isHasImg()) {
                        EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                        epcPartsImageDTO.setImageName(p.getPartsCode());
                        String imageUrl = "oil/" + p.getPartsCode() + ".png";
                        String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.GM, imageUrl);
                        epcPartsImageDTO.setImageUrl(imgPath);
                        epcPartsDTO.setEpcPartsImageDTOs(Lists.newArrayList(epcPartsImageDTO));
                    }
                    return epcPartsDTO;
                }).collect(Collectors.toList());
    }

    private Set<GMBtrPartsDTO> queryBtrParts(Set<String> btrPartsNames, GMVinModelDTO analyseResult) {
        if (StringUtils.isBlank(analyseResult.getBrand())) {
            return Collections.emptySet();
        }
        Set<GMBtrPartsDTO> gmBtrPartsDTOS = gmBtrPartsDAO.queryBtrPartsNameByBrand(analyseResult.getBrandName(), btrPartsNames);
        if (CollectionUtils.isEmpty(gmBtrPartsDTOS)) {
            return Collections.emptySet();
        }
        Set<String> btrPartsName = gmBtrPartsDTOS.stream().map(GMBtrPartsDTO::getBasicPartsName).collect(Collectors.toSet());

        List<GmPartsMappingDTO> partsMappings = queryPartsMappingsForCall(btrPartsName, analyseResult);
        List<GMPartsDetailDTO> partsInfo = queryPartsInfoByPartsMappingForCall(partsMappings, analyseResult);
        partsInfo = filterPartsInfoByVinAnalyseResultForCall(partsInfo, analyseResult);
        Map<String, List<GMPartsDetailDTO>> partsInfoGroupByPartsName = getPartsInfoGroupByPartsNameForCall(partsInfo, partsMappings);
        partsInfoGroupByPartsName = filterPartsInfoByVinAnalyseResultForCall(partsInfoGroupByPartsName, analyseResult);
        List<EPCPartsDTO> epcPartsList = getEPCPartsDTOListForCall(partsInfoGroupByPartsName, analyseResult, null);
        if (CollectionUtils.isEmpty(epcPartsList)) {
            return Collections.emptySet();
        }

        Map<String, List<EPCPartsDTO>> partsMap = epcPartsList.stream().collect(Collectors.groupingBy(EPCPartsDTO::getBtrPartsName));
        return gmBtrPartsDTOS.stream()
                .filter(p -> CollectionUtils.isNotEmpty(partsMap.get(p.getBasicPartsName())))
                .flatMap(p -> {
                    List<EPCPartsDTO> epcPartsDTOS = partsMap.get(p.getBasicPartsName());
                    return epcPartsDTOS.stream().map(l ->
                            GMBtrPartsDTO.builder()
                                    .btrPartsName(p.getBtrPartsName())
                                    .partsCode(l.getPartsCode() + p.getDistinguishingMarks() + p.getSuffix())
                                    .build()
                    );
                }).collect(Collectors.toSet());
    }

    @Override
    protected List<PartsAssemblyDTO> queryPartsAssembly(Set<String> imageCodes, Set<String> partsNames, GMVinModelDTO analyseResult) {
        if (Objects.isNull(analyseResult)) {
            return Collections.emptyList();
        }
        List<GmPartsMappingDTO> partsMappings = queryPartsMappingsForCall(partsNames, analyseResult);
        List<GMPartsDetailDTO> partsInfo = queryPartsInfoByPartsMappingForCall(partsMappings, analyseResult);
        partsInfo = filterPartsInfoByVinAnalyseResultForCall(partsInfo, analyseResult);
        //过滤掉非总成
        partsInfo.removeIf(d -> StringUtils.containsNone(d.getPartNameCN(), "包括"));
        partsInfo.removeIf(d -> imageCodes.stream().noneMatch(s -> StringUtils.equals(s, d.getSubject())));
        Map<String, List<GMPartsDetailDTO>> partsInfoGroupByPartsName = getPartsInfoGroupByPartsNameForCall(partsInfo, partsMappings);
        partsInfoGroupByPartsName = filterPartsInfoByVinAnalyseResultForCall(partsInfoGroupByPartsName, analyseResult);

        if (MapUtils.isEmpty(partsInfoGroupByPartsName)) {
            return Collections.emptyList();
        }
        List<PartsAssemblyDTO> partsImageInfoList = Lists.newArrayList();

        for (String partsName : partsInfoGroupByPartsName.keySet()) {
            List<GMPartsDetailDTO> partsDetailList = partsInfoGroupByPartsName.get(partsName);
            for (GMPartsDetailDTO partsDetail : partsDetailList) {
                String partNameCN = partsDetail.getPartNameCN();
                partNameCN = toDBC(partNameCN);
                String assembly = StringUtils.substringBetween(partNameCN, "包括", ")");
                if (assembly.contains("-")) {
                    assembly = convert(assembly);
                }
                List<String> partsDetailByCallout = partsDetailDAO.findPartsDetailByCallout(assembly, analyseResult.getCatalog(), partsDetail.getSubject());
                if (CollectionUtils.isEmpty(partsDetailByCallout)) {
                    return Collections.emptyList();
                }
                List<GmPartsMappingDTO> gmPartsMappingDTOs = partsMappingDAO.queryByPartsCode(analyseResult.getBrandName(), partsDetailByCallout);
                Set<String> partsNameSet = gmPartsMappingDTOs.stream().map(GmPartsMappingDTO::getBtrPartsName).collect(Collectors.toSet());
                partsNameSet.forEach(d -> partsImageInfoList.add(PartsAssemblyDTO.builder().assPartsName(partsName).imageCode(partsDetail.getSubject()).nonAssPartsName(d).build()));

            }
        }

        return partsImageInfoList;

    }

    private String toDBC(String str) {
        str = str.replaceAll("）", ")");
        str = str.replaceAll("　| ", "");
        str = str.replaceAll("－", "-");
        str = str.replaceAll("，|，", ",");
        str = str.replaceAll("、", ",");
        return str;
    }

    private String convert(String str) {
        List<String> list = Lists.newArrayList(Splitter.on(",").splitToList(str));
        List<String> collect = list.stream().filter(s -> !s.contains("-")).collect(Collectors.toList());

        list.stream().filter(s -> s.contains("-")).forEach(contain -> {
            int i = contain.indexOf("-");
            Integer start = Integer.valueOf(contain.substring(0, i));
            Integer end = Integer.valueOf(contain.substring(i + 1));
            for (int j = start; j <= end; j++) {
                collect.add(String.valueOf(j));
            }
        });

        return Joiner.on(",").join(collect);
    }


}